#include "SDL.h"
#include "SDL_image.h"
#include "Game.h"
#include <random>
#include "SceneMain.h"
#include "SceneTitle.h"
#include "SceneEnd.h"

SceneMain::~SceneMain()
{
}

void SceneMain::update(float deltaTime)
{
    keyboardControl(deltaTime);
    updatePlayerProjectiles(deltaTime);
    updateEnemyProjectiles(deltaTime);
    spawnEnemy();
    updateEnemies(deltaTime);
    updatePlayer(deltaTime);
    updateExplosion(deltaTime);
    updateItem(deltaTime);
    if (isDead) {
        changeSceneDelayed(deltaTime, 3);   // 3秒后切换到结束场景
    }
}

void SceneMain::render()
{
    // render player bullets
    renderPlayerProjectiles();
    renderEnemiyProjectiles();
    // render player
    if (!isDead)
    {
        SDL_Rect playerRect = {
            static_cast<int>(player.position.x),
            static_cast<int>(player.position.y),
            player.width, player.height};
        SDL_RenderCopy(game.getRenderer(), player.texture, NULL, &playerRect);
    }

    // render enemies
    renderEnemies();

    // render explosion
    renderExplosion();

    // render item
    renderItems();

    // render UI health
    renderUI();
}

void SceneMain::handleEvent(SDL_Event *event)
{
}

void SceneMain::init()
{
    // genarate random number
    std::random_device rd;    // 生成随机数种子
    gen = std::mt19937(rd()); // 生成随机数引擎
    // 创建指定分布
    dis = std::uniform_real_distribution<float>(0.0f, 1.0f);
    // 获取随机数
    // auto r = dis(gen);

    // 读取并播放背景音乐
    bgm = Mix_LoadMUS("assets/music/03_Racing_Through_Asteroids_Loop.ogg");
    if (bgm == nullptr) {
        SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to load music: %s", Mix_GetError());
    }
    Mix_PlayMusic(bgm, -1);

    // 读取音效资源
    sounds["player_shoot"] = Mix_LoadWAV("assets/sound/laser_shoot4.wav");
    sounds["enemy_shoot"] = Mix_LoadWAV("assets/sound/xs_laser.wav");
    sounds["player_explode"] = Mix_LoadWAV("assets/sound/explosion1.wav");
    sounds["enemy_explode"] = Mix_LoadWAV("assets/sound/explosion3.wav");
    sounds["hit"] = Mix_LoadWAV("assets/sound/eff11.wav");
    sounds["get_item"] = Mix_LoadWAV("assets/sound/eff5.wav");

    // player texture
    player.texture = IMG_LoadTexture(game.getRenderer(), "assets/image/SpaceShip.png");
    SDL_QueryTexture(player.texture, NULL, NULL, &player.width, &player.height);
    player.width /= 4;
    player.height /= 4;
    player.position.x = game.getWindowWidth() / 2 - player.width / 2;
    player.position.y = game.getWindowHeight() - player.height;

    // UI health texture
    uiHealth = IMG_LoadTexture(game.getRenderer(), "assets/image/Health UI Black.png");
    // load font
    scoreFont = TTF_OpenFont("assets/font/VonwaonBitmap-12px.ttf", 24);

    // 初始化玩家子弹模板
    ProjectilePlayerTemplate.texture = IMG_LoadTexture(game.getRenderer(), "assets/image/laser-3.png");
    SDL_QueryTexture(ProjectilePlayerTemplate.texture, NULL, NULL, &ProjectilePlayerTemplate.width, &ProjectilePlayerTemplate.height);
    ProjectilePlayerTemplate.width /= 4;
    ProjectilePlayerTemplate.height /= 4;

    // 初始化敌机模板
    enemyTemplate.texture = IMG_LoadTexture(game.getRenderer(), "assets/image/insect-2.png");
    SDL_QueryTexture(enemyTemplate.texture, NULL, NULL, &enemyTemplate.width, &enemyTemplate.height);
    enemyTemplate.width /= 4;
    enemyTemplate.height /= 4;

    // 初始化敌机子弹模板
    ProjectileEnemyTemplate.texture = IMG_LoadTexture(game.getRenderer(), "assets/image/bullet-1.png");
    SDL_QueryTexture(ProjectileEnemyTemplate.texture, NULL, NULL, &ProjectileEnemyTemplate.width, &ProjectileEnemyTemplate.height);
    ProjectileEnemyTemplate.width /= 4;
    ProjectileEnemyTemplate.height /= 4;

    // Explosion template
    ExplosionTemplate.texture = IMG_LoadTexture(game.getRenderer(), "assets/effect/explosion.png");
    SDL_QueryTexture(ExplosionTemplate.texture, NULL, NULL, &ExplosionTemplate.width, &ExplosionTemplate.height);
    ExplosionTemplate.totalFrame = ExplosionTemplate.width / ExplosionTemplate.height;
    ExplosionTemplate.width = ExplosionTemplate.height;

    // Item templata
    ItemLiftTemplate.texture = IMG_LoadTexture(game.getRenderer(), "assets/image/bonus_life.png");
    SDL_QueryTexture(ItemLiftTemplate.texture, NULL, NULL, &ItemLiftTemplate.width, &ItemLiftTemplate.height);
    ItemLiftTemplate.width /= 4;
    ItemLiftTemplate.height /= 4;
}

void SceneMain::clean()
{
    // clean projectile container
    for (auto &projectile : projectilesPlayer)
    {
        if (projectile != nullptr)
        {
            delete projectile;
        }
    }
    projectilesPlayer.clear();

    // clean enemy container
    for (auto &enemy : enemies)
    {
        if (enemy != nullptr)
        {
            delete enemy;
        }
    }
    enemies.clear();

    // clean enemy bullet container
    for (auto &ProjectileEnemy : projectilesEnemy)
    {
        if (ProjectileEnemy != nullptr)
        {
            delete ProjectileEnemy;
        }
    }
    projectilesEnemy.clear();

    // clean explosion container
    for (auto &explosion : explosions)
    {
        if (explosion != nullptr)
        {
            delete explosion;
        }
    }
    explosions.clear();

    // clean Item container
    for (auto &item : items)
    {
        if (item != nullptr)
        {
            delete item;
        }
    }
    items.clear();

    // clean sound container
    for (auto sound : sounds) {
        if (sound.second != nullptr) {
            Mix_FreeChunk(sound.second);
        }
    }
    sounds.clear();

    // clean player texture
    if (player.texture != nullptr)
    {
        SDL_DestroyTexture(player.texture);
    }
    // clean projectile template
    if (ProjectilePlayerTemplate.texture != nullptr)
    {
        SDL_DestroyTexture(ProjectilePlayerTemplate.texture);
    }
    // clean enemies template
    if (enemyTemplate.texture != nullptr)
    {
        SDL_DestroyTexture(enemyTemplate.texture);
    }
    // clean enemy bullet template
    if (ProjectileEnemyTemplate.texture != nullptr)
    {
        SDL_DestroyTexture(ProjectileEnemyTemplate.texture);
    }
    // clean explosion template
    if (ExplosionTemplate.texture != nullptr)
    {
        SDL_DestroyTexture(ExplosionTemplate.texture);
    }
    // clean item template
    if (ItemLiftTemplate.texture != nullptr)
    {
        SDL_DestroyTexture(ItemLiftTemplate.texture);
    }

    // clean music
    if (bgm != nullptr) {
        Mix_HaltMusic();
        Mix_FreeMusic(bgm);
    }

    // clean ui health texture
    if (uiHealth != nullptr) {
        SDL_DestroyTexture(uiHealth);
    }

    // clean font
    if (scoreFont != nullptr) {
        TTF_CloseFont(scoreFont);
    }
}

void SceneMain::keyboardControl(float deltaTime)
{
    if (isDead)
    {
        return;
    }
    auto keyboardState = SDL_GetKeyboardState(NULL);
    if (keyboardState[SDL_SCANCODE_W] | keyboardState[SDL_SCANCODE_UP])
    {
        player.position.y -= deltaTime * player.speed;
    }
    if (keyboardState[SDL_SCANCODE_S] | keyboardState[SDL_SCANCODE_DOWN])
    {
        player.position.y += deltaTime * player.speed;
    }
    if (keyboardState[SDL_SCANCODE_A] | keyboardState[SDL_SCANCODE_LEFT])
    {
        player.position.x -= deltaTime * player.speed;
    }
    if (keyboardState[SDL_SCANCODE_D] | keyboardState[SDL_SCANCODE_RIGHT])
    {
        player.position.x += deltaTime * player.speed;
    }

    // Limit position
    if (player.position.x < 0)
        player.position.x = 0;
    if (player.position.x > game.getWindowWidth() - player.width)
        player.position.x = game.getWindowWidth() - player.width;

    if (player.position.y < 0)
        player.position.y = 0;
    if (player.position.y > game.getWindowHeight() - player.height)
        player.position.y = game.getWindowHeight() - player.height;

    // control bullet shoot
    if (keyboardState[SDL_SCANCODE_J])
    {
        auto currentTime = SDL_GetTicks();
        if ((currentTime - player.lastShootTime) > player.coolDown)
        {
            shootPlayer();
            player.lastShootTime = currentTime;
        }
    }
}

void SceneMain::shootPlayer(void)
{
    // 实现子弹发射的逻辑
    auto projectile = new ProjectilePlayer(ProjectilePlayerTemplate);
    projectile->position.x = player.position.x + player.width / 2 - projectile->width / 2;
    projectile->position.y = player.position.y;
    projectilesPlayer.push_back(projectile);
    Mix_PlayChannel(0, sounds["player_shoot"], 0);
}

void SceneMain::updatePlayerProjectiles(float deltaTime)
{
    int margin = 32; // 子弹超出屏幕外边界的距离
    for (auto it = projectilesPlayer.begin(); it != projectilesPlayer.end();)
    {
        auto projectile = *it;
        projectile->position.y -= projectile->speed * deltaTime;
        // 检查子弹是否超出屏幕
        if (projectile->position.y + margin < 0)
        {
            delete projectile;
            it = projectilesPlayer.erase(it);
            // SDL_Log("Player Projectile removed.");
        }
        else
        {
            bool hit = false;
            for (auto enemy : enemies)
            {
                SDL_Rect enemyRect = {
                    static_cast<int>(enemy->position.x),
                    static_cast<int>(enemy->position.y),
                    enemy->width,
                    enemy->height,
                };
                SDL_Rect projectileRect = {
                    static_cast<int>(projectile->position.x),
                    static_cast<int>(projectile->position.y),
                    projectile->width,
                    projectile->height,
                };
                if (SDL_HasIntersection(&enemyRect, &projectileRect))
                {
                    enemy->currentHealth -= projectile->damage;
                    delete projectile;
                    it = projectilesPlayer.erase(it);
                    hit = true;
                    Mix_PlayChannel(-1, sounds["hit"], 0);
                    break;
                }
            }
            if (!hit)
            {
                ++it;
            }
        }
    }
}

void SceneMain::renderPlayerProjectiles()
{
    for (auto projectile : projectilesPlayer)
    {
        SDL_Rect projectileRect = {
            static_cast<int>(projectile->position.x),
            static_cast<int>(projectile->position.y),
            projectile->width,
            projectile->height};
        SDL_RenderCopy(game.getRenderer(), projectile->texture, NULL, &projectileRect);
    }
}

void SceneMain::renderEnemiyProjectiles()
{
    for (auto projectile : projectilesEnemy)
    {
        SDL_Rect projectileRect = {
            static_cast<int>(projectile->position.x),
            static_cast<int>(projectile->position.y),
            projectile->width,
            projectile->height};
        // SDL_RenderCopy(game.getRenderer(), projectile->texture, NULL, &projectileRect);
        float angle = atan2(projectile->direction.y, projectile->direction.x) * 180 / M_PI - 90;
        SDL_RenderCopyEx(game.getRenderer(), projectile->texture, NULL, &projectileRect,
                         angle, NULL, SDL_FLIP_NONE);
    }
}

void SceneMain::renderItems()
{
    for (auto &item : items) {
        SDL_Rect itemRect = {
            static_cast<int>(item->position.x),
            static_cast<int>(item->position.y),
            item->width,
            item->height,
        };
        SDL_RenderCopy(game.getRenderer(), item->texture, NULL, &itemRect);
    }   
}

void SceneMain::renderUI()
{
    int x = 10;
    int y = 10;
    int size = 32;
    int offset = 22;
    SDL_SetTextureColorMod(uiHealth, 100, 100, 100);    // 颜色减淡
    for (int i = 0; i<player.maxHealth; i++) {
        SDL_Rect rect = {x+i*offset, y, size, size};
        SDL_RenderCopy(game.getRenderer(), uiHealth, NULL, &rect);
    }

    SDL_SetTextureColorMod(uiHealth, 255, 255, 255);    // reset color
    for (int i = 0; i<player.currentHealth; i++) {
        SDL_Rect rect = {x+i*offset, y, size, size};
        SDL_RenderCopy(game.getRenderer(), uiHealth, NULL, &rect);
    }

    // render score
    auto text = "Score:" + std::to_string(score);
    SDL_Color color = {255, 255, 255, 255}; // color white
    SDL_Surface* surface = TTF_RenderUTF8_Solid(scoreFont, text.c_str(), color);
    SDL_Texture* texture = SDL_CreateTextureFromSurface(game.getRenderer(), surface);
    SDL_Rect rect = {game.getWindowWidth() - 10 - surface->w, 10, surface->w, surface->h};
    SDL_RenderCopy(game.getRenderer(), texture, NULL, &rect);
    SDL_FreeSurface(surface);
    SDL_DestroyTexture(texture);
}

void SceneMain::spawnEnemy()
{
    if (dis(gen) > 1 / 60.0f)
    {
        return;
    }
    Enemy *enemy = new Enemy(enemyTemplate);
    enemy->position.x = dis(gen) * (game.getWindowWidth() - enemy->width);
    enemy->position.y = -(enemy->height);
    enemies.push_back(enemy);
}

void SceneMain::updatePlayer(float deltaTime)
{
    if (isDead)
    {
        return;
    }
    if (player.currentHealth <= 0)
    {
        // TODO: Game Over
        auto currentTime = SDL_GetTicks();
        isDead = true;
        auto explosion = new Explosion(ExplosionTemplate);
        explosion->position.x = player.position.x + player.width / 2 - explosion->width / 2;
        explosion->position.y = player.position.y + player.height / 2 - explosion->height / 2;
        explosion->startTime = currentTime;
        explosions.push_back(explosion);
        Mix_PlayChannel(-1, sounds["player_explode"], 0);
        game.setFinalScore(score);
        return;
    }
    for (auto enemy : enemies)
    {
        SDL_Rect enemyRect = enemy->getRect();
        SDL_Rect playerRect = player.getRect();
        if (SDL_HasIntersection(&enemyRect, &playerRect))
        {
            player.currentHealth -= 1;
            enemy->currentHealth = 0;
        }
    }
}

void SceneMain::updateEnemies(float deltaTime)
{
    auto currentTime = SDL_GetTicks();
    for (auto it = enemies.begin(); it != enemies.end();)
    {
        auto enemy = *it;
        enemy->position.y += enemy->speed * deltaTime;
        // 检查enemy->position.y是否超出屏幕
        if (enemy->position.y > game.getWindowHeight())
        {
            delete enemy;
            it = enemies.erase(it);
            // SDL_Log("enemy removed.");
        }
        else
        {
            if (((currentTime - enemy->lastShootTime) > enemy->coolDown) && (isDead == false))
            {
                shootEnemy(enemy);
                enemy->lastShootTime = currentTime;
            }
            if (enemy->currentHealth <= 0)
            {
                // delete enemy;
                enemyExplode(enemy);
                it = enemies.erase(it);
            } else {
                ++it;
            }
        }
    }
}

void SceneMain::renderEnemies()
{
    for (auto enemy : enemies)
    {
        SDL_Rect enemyRect = enemy->getRect();
        SDL_RenderCopy(game.getRenderer(), enemy->texture, NULL, &enemyRect);
    }
}

void SceneMain::renderExplosion()
{
    for (auto explosion : explosions)
    {
        SDL_Rect src = {
            explosion->currentFreme * explosion->width,
            0,
            explosion->width,
            explosion->height,
        };
        SDL_Rect dst = {
            static_cast<int>(explosion->position.x),
            static_cast<int>(explosion->position.y),
            explosion->width,
            explosion->height,
        };
        SDL_RenderCopy(game.getRenderer(), explosion->texture, &src, &dst);
    }
}

void SceneMain::shootEnemy(Enemy *enemy)
{
    auto projectile = new ProjectileEnemy(ProjectileEnemyTemplate);
    projectile->position.x = enemy->position.x + enemy->width / 2 - projectile->width / 2;
    projectile->position.y = enemy->position.y + enemy->height / 2 - projectile->height / 2;

    projectile->direction = getDirection(enemy);
    projectilesEnemy.push_back(projectile);
    Mix_PlayChannel(-1, sounds["enemy_shoot"], 0);
}

SDL_FPoint SceneMain::getDirection(Enemy *enemy)
{
    auto x = (player.position.x + player.width) / 2 - (enemy->position.x + enemy->width) / 2;
    auto y = (player.position.y + player.height) / 2 - (enemy->position.y + enemy->height) / 2;
    auto length = sqrt(x * x + y * y);
    x /= length;
    y /= length;
    return SDL_FPoint{x, y};
}

void SceneMain::enemyExplode(Enemy *enemy)
{
    auto currentTime = SDL_GetTicks();
    auto explosion = new Explosion(ExplosionTemplate);
    explosion->position.x = enemy->position.x + enemy->width / 2 - explosion->width / 2;
    explosion->position.y = enemy->position.y + enemy->height / 2 - explosion->height / 2;
    explosion->startTime = currentTime;
    explosions.push_back(explosion);
    if (dis(gen) < 0.5f)
    {
        dropItem(enemy);
    }
    Mix_PlayChannel(-1, sounds["enemy_explode"], 0);
    score += 1;
    delete enemy;
}

void SceneMain::dropItem(Enemy *enemy)
{
    // TODO:
    auto item = new Item(ItemLiftTemplate);
    item->position.x = enemy->position.x + enemy->width / 2 - item->width / 2;
    item->position.y = enemy->position.y + enemy->height / 2 - item->height / 2;
    float angle = dis(gen) * 2 * M_PI;
    item->direction.x = cos(angle);
    item->direction.y = sin(angle);
    items.push_back(item);
}

void SceneMain::playerGetItem(Item *item)
{
    score += 2;
    if (item->type == ItemType::Life) {
        player.currentHealth += 1;
        if (player.currentHealth > player.maxHealth) {
            player.currentHealth = player.maxHealth;
        }
    }
    Mix_PlayChannel(-1, sounds["get_item"], 0);
}

void SceneMain::changeSceneDelayed(float deltaTime, float delay)
{
    timerEnd += deltaTime;
    if (timerEnd > delay) {
        auto sceneEnd = new SceneEnd();
        game.changeScene(sceneEnd);
    }
}

void SceneMain::updateEnemyProjectiles(float deltaTime)
{
    auto margin = 32;
    auto currentTime = SDL_GetTicks();
    for (auto it = projectilesEnemy.begin(); it != projectilesEnemy.end();)
    {
        auto projectile = *it;

        projectile->position.x += projectile->speed * projectile->direction.x * deltaTime;
        projectile->position.y += projectile->speed * projectile->direction.y * deltaTime;

        // 检查子弹是否超出屏幕
        if ((projectile->position.y > game.getWindowHeight() + margin) || (projectile->position.y < -margin) || (projectile->position.x > game.getWindowWidth() + margin) || (projectile->position.x < -margin))
        {
            delete projectile;
            it = projectilesEnemy.erase(it);
            // SDL_Log("enemy removed.");
        }
        else
        {
            SDL_Rect projectileRect = {
                static_cast<int>(projectile->position.x),
                static_cast<int>(projectile->position.y),
                projectile->width,
                projectile->height,
            };
            SDL_Rect playerRect = {
                static_cast<int>(player.position.x),
                static_cast<int>(player.position.y),
                player.width,
                player.height,
            };
            if ((SDL_HasIntersection(&projectileRect, &playerRect)) && (isDead == false))
            {
                player.currentHealth -= (projectile->damage);
                delete projectile;
                it = projectilesEnemy.erase(it);
                Mix_PlayChannel(-1, sounds["hit"], 0);
            }
            else
            {
                ++it;
            }
        }
    }
}

void SceneMain::updateExplosion(float deltaTime)
{
    auto currentTime = SDL_GetTicks();
    for (auto it = explosions.begin(); it != explosions.end();)
    {
        auto explosion = *it;
        explosion->currentFreme = (currentTime - explosion->startTime) * explosion->FPS / 1000;
        if (explosion->currentFreme >= explosion->totalFrame)
        {
            delete explosion;
            it = explosions.erase(it);
        }
        else
        {
            ++it;
        }
    }
}

void SceneMain::updateItem(float deltaTime)
{
    for (auto it = items.begin(); it != items.end();)
    {
        auto item = *it;
        // 更新位置
        item->position.x += item->direction.x * item->speed * deltaTime;
        item->position.y += item->direction.y * item->speed * deltaTime;
        // 处理边缘反弹
        if ((item->position.x < 0) && (item->bounceCount>0)) {
            item->direction.x = -item->direction.x; 
            item->bounceCount--;
        }
        if ((item->position.y < 0) && (item->bounceCount>0)) {
            item->direction.y = -item->direction.y; 
            item->bounceCount--;
        }
        if ((item->position.x > (game.getWindowWidth() - item->width)) 
            && (item->bounceCount>0)) {
            item->direction.x = -item->direction.x; 
            item->bounceCount--;
        }
        if ((item->position.y > (game.getWindowHeight() - item->height)) 
            && (item->bounceCount>0)) {
            item->direction.y = -item->direction.y; 
            item->bounceCount--;
        }
        // 如果超出屏幕范围，则删除
        if (((item->position.x + item->width) < 0 )
            || (item->position.x > game.getWindowWidth()) 
            || (item->position.y + item->height) < 0 
            || (item->position.y > game.getWindowHeight()) )
        {
            delete item;
            it = items.erase(it);
        }
        else
        {
            SDL_Rect itemRect = item->getRect();
            SDL_Rect playerRect = player.getRect();
            
            if (SDL_HasIntersection(&itemRect, &playerRect))
            {
                playerGetItem(item);
                delete item;
                it = items.erase(it);
            }
            else
            {
                ++it;
            }
        }
    }
}

